package com.legendframework.core.dao.yaml.support;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.legendframework.core.dao.Return;
import com.legendframework.core.dao.exception.DaoException;
import com.legendframework.core.dao.transaction.YamlTransaction;
import com.legendframework.core.dao.transaction.enums.TransactionStatus;
import com.legendframework.core.dao.transaction.pojo.IOTransaction;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @Auther: ShallowDream7
 * @Date: 2019/7/31 22:14
 * @Description:
 */
public class BaseYml<T> extends AbstractBaseYml<T> implements IBaseYml<T> {

    private Supplier<Type> typeConverter;

    private Gson gson;

    private final YamlTransaction yamlTransaction;

    /**
     * 事务备份池
     * <p>
     * 对于一个yml文件 可能产生的多个事务备份
     */
    private Map<String, T> transactionBackupsPool;

    public Map<String, T> getTransactionBackupsPool() {
        return this.transactionBackupsPool;
    }

    /**
     * 构造器
     *
     * @param path          文件的绝对路径前缀
     * @param typeConverter 实体类类型转换器
     */
    public BaseYml(String path, Supplier<Type> typeConverter, Class entityType, YamlTransaction yamlTransaction) {
        super(path,entityType);
        this.typeConverter = typeConverter;
        //判断上级目录是否存在
        File file = new File(path).getParentFile();
        if (!file.exists()) {
            //如果不存在则创建该目录
            file.mkdirs();
        }
        existsSave(null);
        gson = new GsonBuilder().serializeNulls().create();
        this.transactionBackupsPool = new HashMap<>();
        this.yamlTransaction = yamlTransaction;
    }

    public BaseYml(String path, Class<T> typeClass, YamlTransaction yamlTransaction) {
        this(path, () -> typeClass, typeClass, yamlTransaction);
    }

    /**
     * 获取锁，如果其他事务未释放锁，则一直等待
     */
    private void transactionLock() {
        IOTransaction<BaseYml<Object>> ioTransaction = getIOTransaction();
        if (ioTransaction != null) {
            try {
                while (!yamlTransaction.getLock().tryLock(ioTransaction.getId(), 10, TimeUnit.MILLISECONDS)) {}
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取当前事务
     *
     * @return
     */
    private IOTransaction<BaseYml<Object>> getIOTransaction() {
        if (yamlTransaction == null) {
            return null;
        }
        return yamlTransaction.getThreadLocalData().peek();
    }

    @Override
    public synchronized void save(T t) {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getIOTransaction();
        if (peek != null && peek.getStatus() == TransactionStatus.BIGIN) {
            //获取锁
            transactionLock();

            //有事务,生成备份
            transactionBackupsPool.put(peek.getId(),t);

            //生成执行链
            peek.getPersistentExecutionChain().computeIfAbsent(path, k -> Return.build((BaseYml) this, IOTransaction.ExecuteType.UPDATE));
            return;
        }
        try {
            if (t != null) {
                //序列化
                toSave(t);
            } else {
                //即使是空也要创建文件
                File file = new File(path);
                if (!file.exists()) {
                    file.createNewFile();
                }
            }
        } catch (IOException e) {
            throw new DaoException("Yaml保存失败", e);
        }
    }

    @Override
    public T get() {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getIOTransaction();
        if (peek != null && peek.getStatus() == TransactionStatus.BIGIN) {
            //判断其他事务是否加锁，如果加锁一直等待
            transactionLock();

            //有事务，获取备份
            T t = transactionBackupsPool.get(peek.getId());
            if (t != null) {
                return t;
            }
        }
        try {
            Object read = toGet();
            T t;
            if (typeConverter != null && read != null) {
                String json = this.gson.toJson(read);
                t = this.gson.fromJson(json, typeConverter.get());
            } else {
                t = (T) read;
            }
            //如果有事务则存入备份
            if (peek != null) {
                transactionBackupsPool.put(peek.getId(), t);
            }
            return t;
        } catch (Throwable e) {
            throw new DaoException("Yaml获取失败", e);
        }
    }

    @Override
    public Boolean isExist() {
        return new File(path).exists();
    }

    @Override
    public void existsSave(T t) {
        if (!isExist()) {
            save(t);
        }
    }

    @Override
    public synchronized void delete() {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getIOTransaction();
        if (peek != null && peek.getStatus() == TransactionStatus.BIGIN) {
            //判断其他事务是否加锁，如果加锁一直等待
            transactionLock();

            //有事务,删除备份
            transactionBackupsPool.remove(peek.getId());

            //生成执行链
            peek.getPersistentExecutionChain().put(path, Return.build((BaseYml) this, IOTransaction.ExecuteType.DELETE));
            return;
        }
        File file = new File(path);
        file.delete();
    }
}
